from django.db.models import TextField
from django.db.models import signals
from django.core.files.base import ContentFile

from markdown import markdown
import cStringIO as StringIO
import Image
import os

class MarkdownTextField (TextField):
    """
    A TextField that automatically implements DB-cached Markdown translation.

    Accepts two additional keyword arguments:

    if allow_html is False, Markdown will be called in safe mode,
    which strips raw HTML (default is allow_html = True).

    if html_field_suffix is given, that value will be appended to the
    field name to generate the name of the non-editable HTML cache
    field.  Default value is "_html".

    NOTE: The MarkdownTextField is not able to check whether the model
    defines any other fields with the same name as the HTML field it
    attempts to add - if there are other fields with this name, a
    database duplicate column error will be raised.

    """

    def __init__(self, *args, **kwargs):
        self._markdown_safe = not kwargs.pop('allow_html', True)
        self._html_field_suffix = kwargs.pop('html_field_suffix', '_html')
        super(MarkdownTextField, self).__init__(self, *args, **kwargs)

    def contribute_to_class(self, cls, name):
        self._html_field = "%s%s" % (name, self._html_field_suffix)
        TextField(editable=False).contribute_to_class(cls, self._html_field)
        super(MarkdownTextField, self).contribute_to_class(cls, name)

    def pre_save(self, model_instance, add):
        value = getattr(model_instance, self.attname)
        html = markdown(value, safe_mode=self._markdown_safe)
        setattr(model_instance, self._html_field, html)
        return value

    def __unicode__(self):
        return self.attname

class AutoImageField(models.ImageField):
    '''
    Given such code:

    class ProductImage(models.Model):
        fullsize = models.ImageField(upload_to= "products/%Y/%m/%d/")

        display = AutoImageField(upload_to= "products/%Y/%m/%d/",prepopulate_from='fullsize', size=(300, 300))
        thumbnail = AutoImageField(upload_to="products/%Y/%m/%d/",null=True,default='/media/noimage.jpg'))

    display will be automatically resized from fullsize, and thumbnail will default to /media/noimage.jpg if no image is uploaded 
    '''

    def __init__(self, *args, **kwargs):
        self.prepopulate_from = kwargs.pop('prepopulate_from', None)
        if self.prepopulate_from:
            self.size = kwargs.pop('size')
        self.default = kwargs.pop('default', None)
        if self.prepopulate_from or self.default:
            kwargs['blank'] = True
        super(AutoImageField, self).__init__(*args, **kwargs)
        
    def _downsize_image(self, instance, name, width, height):
        src = getattr(instance, self.prepopulate_from)
        img = Image.open(src.path)
        img.convert("RGB")
        img.thumbnail((width, height), Image.ANTIALIAS)
        io = StringIO.StringIO()
        img.save(io, 'JPEG')
        
        fname = os.path.split(src.path)[-1].split('.')
        fname.pop()
        fname[-1] = fname[-1] + name
        fname.append('jpg')
        fname = '.'.join(fname)
        return ContentFile(io.getvalue()), fname

    def contribute_to_class(self, cls, name):
        super(AutoImageField, self).contribute_to_class(cls, name)
        
        def process_image(instance, **kwargs):
            if not getattr(instance, name, False):
                width, height = self.size
                img, fname = self._downsize_image(instance, name, width, height)
                getattr(instance, name).save(fname, img, False)
        
        def set_default(instance, **kwargs):
            if not getattr(instance, name, False):
                ff = getattr(instance, name)
                def dummyfunc():
                    return self.default
                ff._get_url = dummyfunc
        
        if self.prepopulate_from:
            signals.pre_save.connect(process_image, sender=cls, weak=False)
        if self.default:
            signals.post_init.connect(set_default, sender=cls, weak=False)